﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;

/*
 * 2013-07-15
 * TCP Asynchronous Server
 * SEAH S.A
 * Jihye Park
*/

namespace TestTcpServer
{
    public partial class formMain : Form
    {
        #region [ Global Variable ]

        delegate void SetTextCallback(int idx, string msg);

        private static object serverLock = new object();
        private static Socket serverSocket;

        byte[] MsgRecvBuffer = new byte[1024];
        byte[] MsgSendBuffer = new byte[1024];

        TextBox[] TextboxArray = new TextBox[3];

        #endregion

        public formMain()
        {
            InitializeComponent();

            InitControl();
        }

        private class ConnectionInfo
        {
            public Socket Socket;
            public byte[] Buffer;
        }

        private static List<ConnectionInfo> connections = new List<ConnectionInfo>();

        // 컨트롤 초기화
        private void InitControl()
        {
            btnListen.Enabled = true;
            btnDisconnect.Enabled = false;
            txtPortNo.Select();             // 포트번호 textbox 선택

            TextboxArray[0] = txtServerStatus;
            TextboxArray[1] = txtContents;
            TextboxArray[2] = txtSend;
        }

        private void SetupServerSocket()
        {
            int _portNo = Int32.Parse(txtPortNo.Text);

            IPEndPoint myEndPoint = new IPEndPoint(IPAddress.Any, _portNo);

            // Create the socket, bind it, and start listening
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Blocking = false;

            serverSocket.Bind(myEndPoint);
            serverSocket.Listen(1);
        }

        public void Start()
        {
            DisplayMessage(0, "Server started...");

            try
            {
                SetupServerSocket();

                for (int i = 0; i < 10; i++)
                {
                    serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);
                }
            }
            catch (Exception ex)
            {
                DisplayMessage(0, "Server start failed...");
            }
        }

        #region [ Button click event ]

        // listen button click event
        private void btnListen_Click(object sender, EventArgs e)
        {
            btnListen.Enabled = false;

            Start();

            btnListen.Enabled = false;
            btnDisconnect.Enabled = true;
            btnSend.Enabled = true;
        }

        // disconnect button click event
        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                serverSocket.Close();
                serverSocket = null;

                DisplayMessage(0, "Server closed.");

                btnListen.Enabled = true;
                btnDisconnect.Enabled = false;
                btnSend.Enabled = false;
            }
            catch (Exception ex)
            {

            }
        }

        // send button click event
        private void btnSend_Click(object sender, EventArgs e)
        {

        }

        #endregion

        // BeginAccept()'s call back function
        public void AcceptCallback(IAsyncResult ar)
        {
            ConnectionInfo connection = new ConnectionInfo();

            try
            {
                // Finish Accept
                Socket s = (Socket)ar.AsyncState;
                connection.Socket = s.EndAccept(ar);
                connection.Socket.Blocking = false;
                connection.Buffer = new byte[1024];
                lock (connections) connections.Add(connection);

                IPEndPoint clientip = (IPEndPoint)connection.Socket.RemoteEndPoint;
                DisplayMessage(0, clientip.ToString() + " connected..!");

                // Start Receive
                connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), connection);
                // Start New Accept
                serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), ar.AsyncState);
            }
            catch (SocketException ex)
            {
                CloseConnection(connection);
            }
            catch (Exception ex)
            {
                CloseConnection(connection);
            }
        }

        // BeginReceive()'s call back function
        public void ReceiveCallback(IAsyncResult ar)
        {
            ConnectionInfo connection = (ConnectionInfo)ar.AsyncState;

            String MsgRecvStr = String.Empty;
            String MsgRecvHexStr = String.Empty;

            try
            {
                // 소켓이 닫혀 있으면, 0을 반환한다.
                int length = connection.Socket.EndReceive(ar);

                if (length > 0)
                {
                    // display decimal data
                    MsgRecvStr = Encoding.Default.GetString(MsgRecvBuffer, 0, length);

                    // display hex data
                    MsgRecvHexStr = ToHexString(MsgRecvStr, "Recv");

                    DisplayMessage(1, "Received Data : " + MsgRecvHexStr + " | " + MsgRecvStr + "\n");

                    connection.Socket.BeginReceive(MsgRecvBuffer, 0, MsgRecvBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), connection);
                }
                else
                {
                    // 소켓이 닫혀 있으면,
                    IPEndPoint clientip = (IPEndPoint)connection.Socket.RemoteEndPoint;
                    DisplayMessage(0, clientip.ToString() + " disconnected...");

                    // Close Socket
                    CloseConnection(connection);
                }
            }
            catch (SocketException ex)
            {
                CloseConnection(connection);
            }
            catch (Exception ex)
            {
                CloseConnection(connection);
            }
        }

        private void CloseConnection(ConnectionInfo ci)
        {
            try
            {
                ci.Socket.Close();
                lock (connections) connections.Remove(ci);
            }
            catch (NullReferenceException ex)
            {

            }
        }

        // Convert the decimal to hex function
        public string ToHexString(string MsgStr, string chk)
        {
            string MsgHexStr = string.Empty;

            if (chk == "Recv")
            {
                for (int i = 0; i < MsgStr.Length; i++)
                {
                    MsgHexStr += MsgRecvBuffer[i].ToString("X2") + " ";
                }
            }
            else if (chk == "Send")
            {
                for (int i = 0; i < MsgStr.Length; i++)
                {
                    MsgHexStr += MsgSendBuffer[i].ToString("X2") + " ";
                }
            }

            return MsgHexStr;
        }

        // Display Textbox Message
        public void DisplayMessage(int idx, string msg)
        {
            try
            {
                if (TextboxArray[idx].InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(DisplayMessage);
                    Invoke(d, new object[] { idx, msg });
                }
                else
                {
                    TextboxArray[idx].AppendText(msg + "\r\n");
                    TextboxArray[idx].ScrollToCaret();
                }
            }
            catch { }
        }
    }
}
